{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1e6c63d4",
   "metadata": {},
   "source": [
    "- 代码里的容器泛指那些专门用来装其他对象的特殊数据类型\n",
    "- 在python中，最常见的内置容器类型有四种：列表、元组、字典、集合"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "047faa32",
   "metadata": {},
   "source": [
    "## 3.1基础知识\n",
    "### 3.1.1 列表常用操作\n",
    "- 列表的创建方法\n",
    "  1. 字面量常量，使用[]创建\n",
    "  2. 内置函数list，可以把任何一个可迭代的对象转换成列表\n",
    "- 对已有列表，要删除其中列表中的某些内容直接使用del语句"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6b99de68",
   "metadata": {},
   "outputs": [],
   "source": [
    "numbers = [1, 2, 3, 4]\n",
    "list(\"foo\")\n",
    "print(numbers[2])\n",
    "print(numbers[1:])\n",
    "del numbers[1:]\n",
    "print(numbers)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "05f99208",
   "metadata": {},
   "source": [
    "1. 在遍历列表时获取下标\n",
    "- 内置函数 **enumerate()** 包裹列表对象\n",
    "- **enumerate**适用于所有可迭代对象，不光可用于列表，还可以是元组，字，字符串等其他对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f8d8595",
   "metadata": {},
   "outputs": [],
   "source": [
    "names = ['foo', 'bar']\n",
    "for index, s in enumerate(names):\n",
    "    print(index, s)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "edd53685",
   "metadata": {},
   "source": [
    "2. 列表推导式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "42fe14fa",
   "metadata": {},
   "outputs": [],
   "source": [
    "results = [n*100 for n in numbers if n %2 == 0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d3bc434",
   "metadata": {},
   "source": [
    "### 3.1.2 理解列表的可变性\n",
    "- python里的内置数据类型，大致可分为可变和不可变两种\n",
    "    - 可变：列表、字典、集合\n",
    "    - 不可变：整数、浮点数、字符串、字节串、元组"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0833988",
   "metadata": {},
   "source": [
    "- 示例一、为字符串追加内容\n",
    "    - 函数外的orig_obj变量所指向的值没有受到任何影响"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f21dc27d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_str(in_func_obj):\n",
    "    print(f'In add [before]: in_func_obj = \"{in_func_obj}\"')\n",
    "    in_func_obj += 'suffix'\n",
    "    print(f'In add [after]: in_func_obj = \"{in_func_obj}\"')\n",
    "\n",
    "orig_obj = 'foo'\n",
    "print(f'Outside [before]: orig_obj=\"{orig_obj}\"')\n",
    "add_str(orig_obj)\n",
    "print(f'Outside [after]: orig_obj = \"{orig_obj}\"')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aad1691e",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "b78b68b9",
   "metadata": {},
   "source": [
    "- 示例二：为列表追加内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c10834e8",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_str(in_func_obj):\n",
    "    print(f'In add [before]: in_func_obj = \"{in_func_obj}\"')\n",
    "    in_func_obj += ['baz']\n",
    "    print(f'In add [after]: in_func_obj = \"{in_func_obj}\"')\n",
    "\n",
    "orig_obj = ['foo', 'bar']\n",
    "print(f'Outside [before]: orig_obj=\"{orig_obj}\"')\n",
    "add_str(orig_obj)\n",
    "print(f'Outside [after]: orig_obj = \"{orig_obj}\"')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8dcfe48",
   "metadata": {},
   "source": [
    "- 示例解释\n",
    "    1. 值传递\n",
    "    2. 引用传递\n",
    "- Python在进行函数调用传参时，采用的既不是值传递，也不是引用传递，而是传递了“变量所指对象的引用”\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4944b22c",
   "metadata": {},
   "source": [
    "### 3.1.3 常用元组操作\n",
    "- 定义方法：\n",
    "    - 字面量表达法和tuple()内置函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb1be6ec",
   "metadata": {},
   "outputs": [],
   "source": [
    "t = (0, 1, 2)\n",
    "t= tuple('foo')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "495d7fad",
   "metadata": {},
   "source": [
    "1. 返回多个结果，其实就是返回元组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "6d01c72f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(100, 20) <class 'tuple'>\n"
     ]
    }
   ],
   "source": [
    "def get_rectangle():\n",
    "    \"\"\"返回长方形的宽和高\"\"\"\n",
    "    width= 100\n",
    "    height= 20\n",
    "    return width, height\n",
    "\n",
    "#获取函数的多个返回值\n",
    "results = get_rectangle()\n",
    "print(results, type(results))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10e01578",
   "metadata": {},
   "source": [
    "2. 没有“元组推导式”\n",
    "   - 没有“元组推导式”，会返回一个生成器对象。调用tuple()函数可以替代推导式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff4de8c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "results = (n*100 for n in range(10) if n%2 == 0)\n",
    "print(results)\n",
    "results = tuple((n*100 for n in range(10) if n%2 == 0))\n",
    "print(results)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32aeeca0",
   "metadata": {},
   "source": [
    "3. 存放结构化数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "738a2b0a",
   "metadata": {},
   "outputs": [],
   "source": [
    "user_info = ('piglei', 'MALE', 30, True)\n",
    "print(user_info[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc09594f",
   "metadata": {},
   "source": [
    "### 3.1.4 具名元组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "916fa0cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import namedtuple\n",
    "Rectangel = namedtuple('Rectangle', 'width, height')\n",
    "rect = Rectangel(width = 100, height=20)\n",
    "print(rect.width)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dee03852",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Rectangel(NamedTuple):\n",
    "    width:int\n",
    "    height:int\n",
    "rect = Rectangel(100, 20)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6322d50",
   "metadata": {},
   "source": [
    "### 3.1.5 字典常用操作\n",
    "1. 遍历字典"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c82d4e8",
   "metadata": {},
   "outputs": [],
   "source": [
    "movie = {'name':'Burning', 'type':'movie', 'year':2018}\n",
    "for key in movie:\n",
    "    print(key, movie[key])\n",
    "for key, value in movie.items():\n",
    "    print(key, value)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7c9358c",
   "metadata": {},
   "source": [
    "2. 访问不存在的字典键\n",
    "    - 边界情况"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3dc98b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "movie = {'name':'Burning', 'type':'movie', 'year':2018}\n",
    "try:\n",
    "    rating = movie['rating']\n",
    "except KeyError:\n",
    "    rating = 0\n",
    "print(movie.get('rating', 0))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdadc846",
   "metadata": {},
   "source": [
    "3. 使用setdefault取值并修改"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d3e12754",
   "metadata": {},
   "outputs": [],
   "source": [
    "d = {'title':'foobar'}\n",
    "d.setdefault('items', []).append('foo')\n",
    "d.setdefault('items', []).append('bar')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a547a0ee",
   "metadata": {},
   "source": [
    "4. 使用pop方法删除不存在的键"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1f7ad6eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "d.pop(key, None)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93d83c96",
   "metadata": {},
   "source": [
    "5. 字典推导式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65a24f4b",
   "metadata": {},
   "outputs": [],
   "source": [
    "d1 = {'foo':3, 'bar':4}\n",
    "{key: value*10 for key, value in d1.items() if key == 'foo'}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "253a47d6",
   "metadata": {},
   "source": [
    "### 3.1.6、认识字典的有序与无序性\n",
    "- “python的字典是无序的”,这种无序现象是由python的底层决定的。\n",
    "- Python里的字典在底层使用了哈希表数据结构。当你往字典里存放一对 **key:value**时，Python会先通过哈希算法计算key的值——一个整形数字；然后根据这个哈希值，决定数据在表里的具体位置\n",
    "- 经过底层优化在3.7的版本之后变成有序\n",
    "- 之前的版本可以导入 **OrderedDict** 变得有序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d7ff222e",
   "metadata": {},
   "outputs": [],
   "source": [
    "d = {}\n",
    "d['First_Key'] = 1\n",
    "d['SECOND_KEY'] = 2\n",
    "\n",
    "for key in d:\n",
    "    print(key)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b051cb2d",
   "metadata": {},
   "source": [
    "### 3.1.7 集合常用操作\n",
    "- 两个特征：去重与无序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "724dc82e",
   "metadata": {},
   "outputs": [],
   "source": [
    "fruits = {'apple', 'orange', 'apple', 'pineapple'}\n",
    "print(fruits)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "210fe959",
   "metadata": {},
   "source": [
    "- 要初始化一个空集合，只能使用set()方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7cd2edfd",
   "metadata": {},
   "outputs": [],
   "source": [
    "empty_set = set()\n",
    "nums = [1, 2, 2, 4, 1]\n",
    "{n for n in nums if n<3}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "205d71ea",
   "metadata": {},
   "source": [
    "1. 不可变的集合 **frozenset**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "405eea93",
   "metadata": {},
   "outputs": [],
   "source": [
    "new_set = set(['foo', 'foo', 'bar'])\n",
    "new_set.add('apple')\n",
    "f_set = frozenset(['foo', 'bar'])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5b322e3",
   "metadata": {},
   "source": [
    "2. 集合运算\n",
    "- 集合最大的独特之处是可以对其进行真正的集合运算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a32f98f9",
   "metadata": {},
   "outputs": [],
   "source": [
    "fruits_1 = {'apple', 'orange', 'pineapple'}\n",
    "fruits_2 = {'tomato', 'orange', 'graps', 'mango'}\n",
    "fruits_1 & fruits_2\n",
    "fruits_1.intersection(fruits_2)\n",
    "fruits_1 | fruits_2\n",
    "fruits_1.union(fruits_2)\n",
    "fruits_1 - fruits_2 "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a4c7be1",
   "metadata": {},
   "source": [
    "3. 集合只能存放可哈希对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "db2e0849",
   "metadata": {},
   "outputs": [],
   "source": [
    "valid_set = {'apple', 30, 1.3, ('foo',)}\n",
    "invalid_set = {'foo', [1,2,3]}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1129866",
   "metadata": {},
   "source": [
    "### 3.1.8 了解对象的可哈希性\n",
    "- 使用 **hash** 函数就可判断是否可哈希"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bf090360",
   "metadata": {},
   "outputs": [],
   "source": [
    "hash({'key':'value'})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d259c6f0",
   "metadata": {},
   "source": [
    "- 只有可哈希的对象，才能放进集合或作为字典的键使用\n",
    "- 所有不可变内置类型都可哈希\n",
    "- 所有可变内置类型都不可哈希\n",
    "- 对于不可变容器类型，仅当他的成员都不可变时，才可哈希\n",
    "- 用户定义的类型默认都是可哈希的"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3132e218",
   "metadata": {},
   "source": [
    "### 3.1.9 深拷贝与浅拷贝\n",
    "1. 浅拷贝"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8994e350",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 30, 4]\n",
      "[1, 2, 3, 4]\n"
     ]
    }
   ],
   "source": [
    "nums = [1, 2, 3, 4]\n",
    "nums_copy = nums\n",
    "nums[2] = 30\n",
    "print(nums_copy)\n",
    "#使用copy函数\n",
    "import copy\n",
    "nums1 = [1, 2, 3, 4]\n",
    "nums_copy1 = copy.copy(nums1)\n",
    "nums[2] = 30\n",
    "print(nums_copy1)\n",
    "#使用推导式浅拷贝\n",
    "d = {'foo':1}\n",
    "d2 = {key: value for key, value in d.items}\n",
    "d['foo'] = 2\n",
    "print(d2)\n",
    "#支持切片操作的容器类型——比如列表、元组、对其进行全切片也可以使用浅拷贝效果\n",
    "nums_copy = nums[:]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49d47b9c",
   "metadata": {},
   "source": [
    "2. 深拷贝\n",
    "- 对于一层层嵌套的复杂数据，浅拷贝无法解决嵌套对象被修改的问题"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "91f33537",
   "metadata": {},
   "outputs": [],
   "source": [
    "items = [1, ['foo', 'bar'], 2, 3]\n",
    "import copy\n",
    "items_copy = copy.copy(items)\n",
    "items_copy1 = copy.deepcopy(items)\n",
    "items[0] = 100\n",
    "items[1].append('xxx')\n",
    "print(items)\n",
    "print(items_copy1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37a7a6e3",
   "metadata": {},
   "source": [
    "## 3.2 案例故事"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9bc34d41",
   "metadata": {},
   "outputs": [],
   "source": [
    "from enum import Enum\n",
    "\n",
    "class PagePerfLevel(str, Enum):\n",
    "    LT_100 = 'Less than 100 ms'\n",
    "    LT_300 = 'Between 100 and 300 ms'\n",
    "    LT_1000 = 'Between 300 ms and 1 s'\n",
    "    GT_1000 = 'Greater than 1 s'\n",
    "\n",
    "def analyze_v1():\n",
    "    path_groups = {}\n",
    "    with open(\"logs.txt\", \"r\") as fp:\n",
    "        for line in fp:\n",
    "            path, time_cost_str = line.strip().split()\n",
    "\n",
    "            #根据页面耗时计算性能等级\n",
    "            time_cost = int(time_cost_str)\n",
    "            if time_cost < 100:\n",
    "                level = PagePerfLevel.LT_100\n",
    "            elif time_cost<300:\n",
    "                level = PagePerfLevel.Lt_300\n",
    "            elif time_cost < 1000:\n",
    "                level = PagePerfLevel.LT_1000\n",
    "            else:\n",
    "                level = PagePerfLevel.GT_1000\n",
    "            \n",
    "            #如果路径第一次出现，存入初始值\n",
    "            if path not in path_groups:\n",
    "                path_groups[path] = {}\n",
    "\n",
    "            #如果性能level第一次出现，存入初始值1\n",
    "            try:\n",
    "                path_groups[path][level]+=1\n",
    "            except KeyError:\n",
    "                path_groups[path][level]=1\n",
    "            \n",
    "    for path, result in path_groups.items():\n",
    "        print(f'== Path: {path}')\n",
    "        total = sum(result.values())\n",
    "        print(f'    Total requests: {total}')\n",
    "        print(f'    Performance')\n",
    "        #在输出结果前，按照“性能等级”在PagePerfLevel里面的循序排序，小于100ms的在最前面\n",
    "        sorted_items = sorted(\n",
    "            result.items(), key = lambda pair:list(PagePerfLevel).index(pair[0])\n",
    "        )\n",
    "        for level_name, count in sorted_items:\n",
    "            print(f'    -{level_name}: {count}')\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    \n",
    "    analyze_v1()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65da974e",
   "metadata": {},
   "source": [
    "1. 使用defaultdict类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8c89db84",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "defaultdict(<class 'int'>, {'foo': 1})\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'foo': 1}"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "int_dict = defaultdict(int)\n",
    "int_dict['foo']+=1\n",
    "print(int_dict)\n",
    "dict(int_dict)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5770ae6b",
   "metadata": {},
   "source": [
    "2. 使用MutableMapping创建自定义字典类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "714f90cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections.abc import MutableMapping\n",
    "\n",
    "class PerfLevelDict(MutableMapping):\n",
    "    \"\"\"存储响应时间性能等级的字典\"\"\"\n",
    "    def __init__(self):\n",
    "        self.data = defaultdict(int)\n",
    "    def __getitem__(self, key):\n",
    "        \"\"\"当某个级别不存在时， 默认返回0\"\"\"\n",
    "        return self.data[self.compute_level(key)]\n",
    "    def __setitem__(self, key, value):\n",
    "        \"\"\"将key转换为对应的性能等级，然后设置值\"\"\"\n",
    "        self.data[self.compute_level(key)] = value\n",
    "    def __delitem__(self, key):\n",
    "        del self.data[key]\n",
    "    def __iter__(self):\n",
    "        return iter(self.data)\n",
    "    def __len__(self):\n",
    "        return len(self.data)\n",
    "    \n",
    "    @staticmethod\n",
    "    def compute_level(time_cost_str):\n",
    "        \"\"\"根据响应时间计算性能等级\"\"\"\n",
    "        #假如已经是性能等级， 不做转换直接返回\n",
    "        if time_cost_str in list(PagePerfLevel):\n",
    "            return time_cost_str\n",
    "        time_cost = int(time_cost_str)\n",
    "        if time_cost < 100:\n",
    "            return PagePerfLevel.LT_100\n",
    "        elif time_cost<300:\n",
    "            return PagePerfLevel.Lt_300\n",
    "        elif time_cost < 1000:\n",
    "            return PagePerfLevel.LT_1000\n",
    "        else:\n",
    "            return PagePerfLevel.GT_1000\n",
    "                             "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f15cbb3e",
   "metadata": {},
   "source": [
    "3. 代码重构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f693ec8d",
   "metadata": {},
   "outputs": [],
   "source": [
    "from enum import Enum\n",
    "from collections import defaultdict\n",
    "from collections.abc import MutableMapping\n",
    "\n",
    "class PagePerfLevel(str, Enum):\n",
    "    LT_100 = 'Less than 100 ms'\n",
    "    LT_300 = 'Between 100 and 300 ms'\n",
    "    LT_1000 = 'Between 300 ms and 1 s'\n",
    "    GT_1000 = 'Greater than 1 s'\n",
    "class PerfLevelDict(MutableMapping):\n",
    "    \"\"\"存储响应时间性能等级的字典\"\"\"\n",
    "    def __init__(self):\n",
    "        self.data = defaultdict(int)\n",
    "    def __getitem__(self, key):\n",
    "        \"\"\"当某个级别不存在时， 默认返回0\"\"\"\n",
    "        return self.data[self.compute_level(key)]\n",
    "    def __setitem__(self, key, value):\n",
    "        \"\"\"将key转换为对应的性能等级，然后设置值\"\"\"\n",
    "        self.data[self.compute_level(key)] = value\n",
    "    def __delitem__(self, key):\n",
    "        del self.data[key]\n",
    "    def __iter__(self):\n",
    "        return iter(self.data)\n",
    "    def __len__(self):\n",
    "        return len(self.data)\n",
    "    def items(self):\n",
    "        \"\"\"按照顺序返回性能等级数据\"\"\"\n",
    "        return sorted(\n",
    "            self.data.items(), key = lambda pair:list(PagePerfLevel).index(pair[0])\n",
    "        )\n",
    "    def total_requests(self):\n",
    "        \"\"\"返回总请求数\"\"\"\n",
    "        return sum(self.values())\n",
    "    @staticmethod\n",
    "    def compute_level(time_cost_str):\n",
    "        \"\"\"根据响应时间计算性能等级\"\"\"\n",
    "        #假如已经是性能等级， 不做转换直接返回\n",
    "        if time_cost_str in list(PagePerfLevel):\n",
    "            return time_cost_str\n",
    "        time_cost = int(time_cost_str)\n",
    "        if time_cost < 100:\n",
    "            return PagePerfLevel.LT_100\n",
    "        elif time_cost<300:\n",
    "            return PagePerfLevel.Lt_300\n",
    "        elif time_cost < 1000:\n",
    "            return PagePerfLevel.LT_1000\n",
    "        else:\n",
    "            return PagePerfLevel.GT_1000\n",
    "def analyze_v2():\n",
    "    path_groups = defaultdict(PerfLevelDict)\n",
    "    with open(\"logs.txt\", \"r\") as fp:\n",
    "        for line in fp:\n",
    "            path, time_cost = line.strip().split()\n",
    "            path_groups[path][time_cost] += 1\n",
    "            \n",
    "    for path, result in path_groups.items():\n",
    "        print(f'== Path: {path}')\n",
    "        print(f'    Total requests: {result.total_requests()}')\n",
    "        print(f'    Performance:')\n",
    "\n",
    "        for level_name, count in result.items():\n",
    "            print(f'    -{level_name}: {count}')\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    analyze_v2()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1a81bfa",
   "metadata": {},
   "source": [
    "## 3.3 编程建议\n",
    "### 3.3.1 用按需返回替代容器\n",
    "- range()函数的迭代，代表一种编程思想：按需生成，而不是一次性返回\n",
    "1. 生成器简介"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54a2b39b",
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_even(max_number):\n",
    "    \"\"\"一个简单的生成器， 返回0到max_number之间的所有偶数\"\"\"\n",
    "    for i in range(0, max_number):\n",
    "        if i%2 == 0:\n",
    "            yield i\n",
    "\n",
    "for i in generate_even(10):\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02e70104",
   "metadata": {},
   "source": [
    "- yield和return 的最大不同之处在于：return 的返回是一次性的，使用它会直接中断整个函数执行，而yield可以逐步给调用方生成结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e2ab5a71",
   "metadata": {},
   "outputs": [],
   "source": [
    "i = generate_even(10)\n",
    "print(next(i))\n",
    "print(next(i))\n",
    "list(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82c397f5",
   "metadata": {},
   "source": [
    "2. 用生成器替代列表\n",
    "- 传统列表：\n",
    "    - 如果item过大，执行会非常满\n",
    "    - 函数调用方如果想在某个processed_item对象满足特定条件时中断，不再处理后面的对象，现在的batch_process函数做不到，它每次都是一次性处理完所有items完成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "09a17821",
   "metadata": {},
   "outputs": [],
   "source": [
    "def batch_process(items):\n",
    "    \"\"\"\n",
    "    批量处理多个items对象\n",
    "    \"\"\"\n",
    "    #初始化空结果列表\n",
    "    results = []\n",
    "    for item in items:\n",
    "        #处理item,可能需要耗费大量时间\n",
    "        #processed_item = ...\n",
    "        results.append(processed_item)\n",
    "    #将拼装后的结果列表返回\n",
    "    return results\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6eb1bdb4",
   "metadata": {},
   "outputs": [],
   "source": [
    "def batch_process(items):\n",
    "    for item in items:\n",
    "        #处理item,可能需要耗费大量时间\n",
    "        #processed_item = ...\n",
    "        yield processed_item\n",
    "for processed_item in batch_process(items):\n",
    "    #如果某个已处理对象过期了，就中断当前的所有处理\n",
    "    if processed_item.has_expired():\n",
    "        break        "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c7cbc4e",
   "metadata": {},
   "source": [
    "### 3.3.2 了解容器的底层实现\n",
    "1. 避开列表的性能陷阱"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "891a89df",
   "metadata": {},
   "outputs": [],
   "source": [
    "def list_apend():\n",
    "    \"\"\"不断往尾部追加\"\"\"\n",
    "    l = []\n",
    "    for i in range(5000):\n",
    "        l.append(i)\n",
    "\n",
    "def list_insert():\n",
    "    \"\"\"不断往头部插入\"\"\"\n",
    "    l = []\n",
    "    for i in range(5000):\n",
    "        l.insert(0, i)\n",
    "\n",
    "import timeit\n",
    "\n",
    "append_spent = timeit.timeit(\n",
    "    setup = 'from __main__ import list_append',\n",
    "    stmt = 'list_append()',\n",
    "    number = 10000,\n",
    ")\n",
    "print(\"list_append:\", append_spent)\n",
    "\n",
    "insert_spent = timeit.timeit(\n",
    "    setup = 'from __main__ import list_insert',\n",
    "    stmt = 'list_insert()',\n",
    "    number = 10000,\n",
    ")\n",
    "print(\"list_insert:\", insert_spent)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88ed9d0f",
   "metadata": {},
   "source": [
    "- Python在实现列表时，底层使用了数组数据结构。该结构最大的特点是，当你在数组中间插入新成员是，该成员之后的其他成员都要移动位置，才操作的平均时间复杂度是O(n)，在列表的头部插入成员，比在尾部追加要慢得多（后者的时间复杂度为O(1)）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "487f9208",
   "metadata": {},
   "source": [
    "- 经常往列表头部追加对象时，最好使用collections.deque类型替代列表,其底层使用了双端列表，无论头部还是尾部最佳，时间复杂度都是O(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd36af89",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import deque\n",
    "\n",
    "def deque_insert():\n",
    "    l = deque()\n",
    "    for i in range(5000):\n",
    "        l.appendleft(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac7ac4d8",
   "metadata": {},
   "source": [
    "2. 使用集合判断成员是否存在\n",
    "- 集合底层是哈希表数据结构，判断是否存在时间复杂度仅为O(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53cd836c",
   "metadata": {},
   "outputs": [],
   "source": [
    "VALID_NAMES = ['piglei', 'raymod', 'bojack', 'caroline']\n",
    "VALID_NAMES_SET = set(VALID_NAMES)\n",
    "def validate_name(name):\n",
    "    if name not in VALID_NAMES_SET:\n",
    "        raise ValueError(f\"{name} is not a valid name!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "08ce6cf6",
   "metadata": {},
   "source": [
    "### 3.3.3 掌握如何快速合并字典\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "1f5ff17b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'name': 'apple', 'price': 10}\n"
     ]
    }
   ],
   "source": [
    "d1 = {'name': 'apple'}\n",
    "d2 = {'price': 10}\n",
    "d1.update(d2)\n",
    "print(d1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "11460d3b",
   "metadata": {},
   "outputs": [],
   "source": [
    "def merge_dict(d1, d2):\n",
    "    #因为字典是可修改的对象，为了避免修改原对象，此处需要复制一个d1的浅拷贝对象\n",
    "    result = d1.copy()\n",
    "    result.update(d2)\n",
    "    return result"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6746b333",
   "metadata": {},
   "source": [
    "- 副作用：修改字典d的原始内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4fce5883",
   "metadata": {},
   "outputs": [],
   "source": [
    "#动态解包字典\n",
    "d1 = {'name': 'apple'}\n",
    "d2 = {'price': 10}\n",
    "print({**d1, **d2})\n",
    "#Python 3.9以后，|操作合并字典\n",
    "print(d1|d2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afb10b69",
   "metadata": {},
   "source": [
    "### 3.3.4 使用有序字典去重"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f577ae69",
   "metadata": {},
   "source": [
    "- 使用集合去重会丢失集合内成员原来的顺序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "05963717",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{3, 10, 2, 21}\n"
     ]
    }
   ],
   "source": [
    "nums = [10, 2, 3, 21, 10, 3]\n",
    "print(set(nums))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0aface37",
   "metadata": {},
   "source": [
    "- 使用有序字典OrderedDict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82e5269b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import OrderedDict\n",
    "list(OrderedDict.fromkeys(nums).keys())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be175c3b",
   "metadata": {},
   "source": [
    "### 3.3.5 别在遍历列表时同步修改"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "77f5c953",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 7, 8, 11]\n"
     ]
    }
   ],
   "source": [
    "def remove_even(numbers):\n",
    "    \"\"\"去掉表里所有的偶数\"\"\"\n",
    "    for number in numbers:\n",
    "        if number % 2 == 0:\n",
    "            #有问题的代码\n",
    "            numbers.remove(number)\n",
    "numbers = [1, 2, 7, 4, 8, 11]\n",
    "remove_even(numbers)\n",
    "print(numbers)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "71ce832a",
   "metadata": {},
   "source": [
    "### 3.3.6 编写推导式的两个“不要”\n",
    "1. 别写太复杂推导式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a356feb8",
   "metadata": {},
   "outputs": [],
   "source": [
    "results = [\n",
    "    task.result if task.result_version == VERSION_2 else gt_legacy_result(task)\n",
    "    for tasks_group in tasks\n",
    "    for task in tasks_group\n",
    "    if task.is_active() and task.has_completed()\n",
    "]\n",
    "######################################################################################\n",
    "results = []\n",
    "for tasks_group in tasks:\n",
    "    if not (task.is_active() and task.has_completed()):\n",
    "        continue\n",
    "\n",
    "    if task.result_version == VERSION_2:\n",
    "        result = task.result\n",
    "    else:\n",
    "        result = task.result\n",
    "    results.append(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23af079f",
   "metadata": {},
   "source": [
    "2. 别把推导式当作代码量更少的循环"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5ae80f56",
   "metadata": {},
   "outputs": [],
   "source": [
    "[process(task) for task in tasks if not task.started]\n",
    "####################################################\n",
    "for task in tasks:\n",
    "    if not task.started:\n",
    "        process(task)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34997677",
   "metadata": {},
   "source": [
    "### 3.3.7 让函数返回NamedTuple\n",
    "- 对于未来可能会变动的多返回值函数来说，使用NamedTuple类型对返回值结果进行建模"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50232d54",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import NamedTuple\n",
    "\n",
    "class Address(NamedTuple):\n",
    "    \"\"\"地址信息结果\"\"\"\n",
    "    country : str\n",
    "    province: str\n",
    "    city: str\n",
    "\n",
    "def lation_to_adress(lat, lon):\n",
    "    return Address(\n",
    "        country=country,\n",
    "        province=province,\n",
    "        city = city\n",
    "    )\n",
    "addr = lation_to_adress(lat, lon)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
